import fs from 'fs'

// Log entry structure
interface LogEntry {
  timestamp: string
  level: string
  message: string
  data?: any
  lineNumber: number
}

// State machine states for parsing log lines
enum ParseState {
  TIMESTAMP = 'timestamp',
  LEVEL = 'level', 
  MESSAGE = 'message',
  DATA = 'data',
  COMPLETE = 'complete',
  ERROR = 'error'
}

// Log analysis results
interface LogAnalysis {
  totalLines: number
  validLogs: number
  invalidLogs: number
  levelCounts: Record<string, number>
  timeRange: {
    start?: string
    end?: string
  }
  errorPatterns: string[]
  entries: LogEntry[]
}

// State machine for parsing log lines
class LogStateMachine {
  private state: ParseState = ParseState.TIMESTAMP
  private currentEntry: Partial<LogEntry> = {}
  private buffer: string = ''
  private bracketDepth: number = 0

  reset() {
    this.state = ParseState.TIMESTAMP
    this.currentEntry = {}
    this.buffer = ''
    this.bracketDepth = 0
  }

  // Process a character through the state machine
  processChar(char: string): boolean {
    switch (this.state) {
      case ParseState.TIMESTAMP:
        return this.processTimestamp(char)
      case ParseState.LEVEL:
        return this.processLevel(char)
      case ParseState.MESSAGE:
        return this.processMessage(char)
      case ParseState.DATA:
        return this.processData(char)
      default:
        return false
    }
  }

  private processTimestamp(char: string): boolean {
    if (char === '[') {
      this.bracketDepth++
      if (this.bracketDepth === 1) {
        this.buffer = ''
        return true
      }
    }
    
    if (char === ']' && this.bracketDepth === 1) {
      this.currentEntry.timestamp = this.buffer.trim()
      this.state = ParseState.LEVEL
      this.buffer = ''
      this.bracketDepth = 0
      return true
    }
    
    if (this.bracketDepth === 1) {
      this.buffer += char
    }
    
    return true
  }

  private processLevel(char: string): boolean {
    if (char === '[') {
      this.bracketDepth++
      if (this.bracketDepth === 1) {
        this.buffer = ''
        return true
      }
    }
    
    if (char === ']' && this.bracketDepth === 1) {
      this.currentEntry.level = this.buffer.trim()
      this.state = ParseState.MESSAGE
      this.buffer = ''
      this.bracketDepth = 0
      return true
    }
    
    if (this.bracketDepth === 1) {
      this.buffer += char
    } else if (this.bracketDepth === 0 && char.trim()) {
      // Skip whitespace between brackets
      return char === ' ' || char === '\t'
    }
    
    return true
  }

  private processMessage(char: string): boolean {
    if (char === '[') {
      this.bracketDepth++
      if (this.bracketDepth === 1) {
        this.buffer = ''
        return true
      }
    }
    
    if (char === ']' && this.bracketDepth === 1) {
      this.currentEntry.message = this.buffer.trim()
      this.state = ParseState.DATA
      this.buffer = ''
      this.bracketDepth = 0
      return true
    }
    
    if (this.bracketDepth === 1) {
      this.buffer += char
    } else if (this.bracketDepth === 0 && char.trim()) {
      // Skip whitespace between brackets
      return char === ' ' || char === '\t'
    }
    
    return true
  }

  private processData(char: string): boolean {
    if (char === ':' && this.buffer.trim() === '') {
      // Found the colon separator, start collecting data
      this.buffer = ''
      return true
    }
    
    this.buffer += char
    return true
  }

  // Parse a complete line using the state machine
  parseLine(line: string, lineNumber: number): LogEntry | null {
    this.reset()
    this.currentEntry.lineNumber = lineNumber

    // Process each character through the state machine
    for (const char of line) {
      if (!this.processChar(char)) {
        this.state = ParseState.ERROR
        break
      }
    }

    // Handle any remaining data buffer
    if (this.state === ParseState.DATA && this.buffer.trim()) {
      try {
        const dataStr = this.buffer.trim()
        if (dataStr.startsWith('"') && dataStr.endsWith('"')) {
          // Try to parse as JSON string
          this.currentEntry.data = JSON.parse(dataStr)
        } else if (dataStr.startsWith('{') || dataStr.startsWith('[')) {
          // Try to parse as JSON object/array
          this.currentEntry.data = JSON.parse(dataStr)
        } else {
          // Store as plain text
          this.currentEntry.data = dataStr
        }
        if (typeof this.currentEntry.data === 'string') {
          this.currentEntry.data = JSON.parse(this.currentEntry.data)
        }
      } catch {
        // If JSON parsing fails, store as plain text
        this.currentEntry.data = this.buffer.trim()
      }
    }

    // Validate that we have the required fields
    if (this.currentEntry.timestamp && this.currentEntry.level && this.currentEntry.message) {
      this.state = ParseState.COMPLETE
      return this.currentEntry as LogEntry
    }

    return null
  }

  getState(): ParseState {
    return this.state
  }
}

// Main log analyzer class
class LogAnalyzer {
  private stateMachine: LogStateMachine = new LogStateMachine()

  // Analyze log file and return structured results
  analyzeLogFile(logFilePath: string): LogAnalysis {
    const logFile = fs.readFileSync(logFilePath, 'utf-8')
    const lines = logFile.split('\n').filter(line => line.trim())
    
    const analysis: LogAnalysis = {
      totalLines: lines.length,
      validLogs: 0,
      invalidLogs: 0,
      levelCounts: {},
      timeRange: {},
      errorPatterns: [],
      entries: []
    }

    // Process each line through the state machine
    lines.forEach((line, index) => {
      const entry = this.stateMachine.parseLine(line, index + 1)
      
      if (entry) {
        analysis.validLogs++
        analysis.entries.push(entry)
        
        // Count log levels
        analysis.levelCounts[entry.level] = (analysis.levelCounts[entry.level] || 0) + 1
        
        // Track time range
        if (!analysis.timeRange.start || entry.timestamp < analysis.timeRange.start) {
          analysis.timeRange.start = entry.timestamp
        }
        if (!analysis.timeRange.end || entry.timestamp > analysis.timeRange.end) {
          analysis.timeRange.end = entry.timestamp
        }
        
        // Collect error patterns
        if (entry.level.toLowerCase().includes('error') || entry.level.toLowerCase().includes('err')) {
          const pattern = entry.message
          if (!analysis.errorPatterns.includes(pattern)) {
            analysis.errorPatterns.push(pattern)
          }
        }
      } else {
        analysis.invalidLogs++
        console.warn(`Failed to parse line ${index + 1}: ${line}`)
      }
    })

    return analysis
  }

  // Filter logs by level
  filterByLevel(entries: LogEntry[], level: string): LogEntry[] {
    return entries.filter(entry => entry.level.toLowerCase() === level.toLowerCase())
  }

  // Filter logs by time range
  filterByTimeRange(entries: LogEntry[], startTime: string, endTime: string): LogEntry[] {
    return entries.filter(entry => 
      entry.timestamp >= startTime && entry.timestamp <= endTime
    )
  }

  // Search logs by message pattern
  searchByMessage(entries: LogEntry[], pattern: string): LogEntry[] {
    const regex = new RegExp(pattern, 'i')
    return entries.filter(entry => regex.test(entry.message))
  }

  // Get log statistics
  getStatistics(entries: LogEntry[]) {
    const stats = {
      totalEntries: entries.length,
      levelDistribution: {} as Record<string, number>,
      messageFrequency: {} as Record<string, number>,
      timelineStats: {
        firstEntry: entries[0]?.timestamp,
        lastEntry: entries[entries.length - 1]?.timestamp,
        avgEntriesPerHour: 0
      }
    }

    // Calculate level distribution
    entries.forEach(entry => {
      stats.levelDistribution[entry.level] = (stats.levelDistribution[entry.level] || 0) + 1
      stats.messageFrequency[entry.message] = (stats.messageFrequency[entry.message] || 0) + 1
    })

    // Calculate average entries per hour
    if (stats.timelineStats.firstEntry && stats.timelineStats.lastEntry) {
      const start = new Date(stats.timelineStats.firstEntry)
      const end = new Date(stats.timelineStats.lastEntry)
      const hours = (end.getTime() - start.getTime()) / (1000 * 60 * 60)
      stats.timelineStats.avgEntriesPerHour = hours > 0 ? entries.length / hours : 0
    }

    return stats
  }
}

/**
 * 分析日志文件, 将日志文件转换为 json 格式
 * @param logFilePath 日志文件路径
 * @returns 分析结果
 */
export default function logJson(logFilePath: string) {
  const analyzer = new LogAnalyzer()
  
  try {
    console.log(`🔍 Analyzing log file: ${logFilePath}`)
    const analysis = analyzer.analyzeLogFile(logFilePath)
    
    // Print analysis summary
    console.log('\n📊 Log Analysis Summary:')
    console.log(`Total lines: ${analysis.totalLines}`)
    console.log(`Valid log entries: ${analysis.validLogs}`)
    console.log(`Invalid lines: ${analysis.invalidLogs}`)
    console.log(`Success rate: ${((analysis.validLogs / analysis.totalLines) * 100).toFixed(1)}%`)
    
    // Print level distribution
    console.log('\n📈 Log Level Distribution:')
    Object.entries(analysis.levelCounts).forEach(([level, count]) => {
      const percentage = ((count / analysis.validLogs) * 100).toFixed(1)
      console.log(`  ${level.padEnd(10)}: ${count.toString().padStart(5)} (${percentage}%)`)
    })
    
    // Print time range
    if (analysis.timeRange.start && analysis.timeRange.end) {
      console.log('\n⏰ Time Range:')
      console.log(`  Start: ${analysis.timeRange.start}`)
      console.log(`  End:   ${analysis.timeRange.end}`)
    }
    
    // Print error patterns
    if (analysis.errorPatterns.length > 0) {
      console.log('\n❌ Error Patterns Found:')
      analysis.errorPatterns.forEach((pattern, index) => {
        console.log(`  ${index + 1}. ${pattern}`)
      })
    }
    
    // Get additional statistics
    const stats = analyzer.getStatistics(analysis.entries)
    console.log('\n📊 Additional Statistics:')
    console.log(`  Average entries per hour: ${stats.timelineStats.avgEntriesPerHour.toFixed(1)}`)
    console.log(`  Unique messages: ${Object.keys(stats.messageFrequency).length}`)
    
    // Export to JSON
    const outputPath = logFilePath.replace(/\.[^/.]+$/, '') + '_analysis.json'
    fs.writeFileSync(outputPath, JSON.stringify(analysis, null, 2))
    console.log(`\n✅ Analysis saved to: ${outputPath}`)
    
    return analysis
    
  } catch (error) {
    console.error('❌ Error analyzing log file:', error)
    throw error
  }
}